レイトレを Raspberry Pi Pico2 の実機上で動かす
https://gyazo.com/3e12c296e6a581ce0d7cf1406999253b
Raspberry Pi Pico2 の実機上でレイトレを動かしてみる。
以下変更箇所。ライブラリ関数を足したり(libmincaml.S, min-rt.ml)グローバル変数(globals.s)を足したりする。
code:diff
diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt
index c0ebbc7..1a90725 100644
--- a/firmware/CMakeLists.txt
+++ b/firmware/CMakeLists.txt
@@ -13,6 +13,8 @@ pico_sdk_init()
add_executable(min_caml_firmware
src/min-caml-start.s
+ src/libmincaml.S
+ src/globals.s
src/main.c
)
diff --git a/firmware/src/globals.s b/firmware/src/globals.s
new file mode 100644
index 0000000..89a22e8
--- /dev/null
+++ b/firmware/src/globals.s
@@ -0,0 +1,36 @@
+ .data
+ .comm min_caml_objects, 480, 3
+ .comm min_caml_size, 16, 3
+ .comm min_caml_dbg, 8, 3
+ .comm min_caml_screen, 24, 3
+ .comm min_caml_vp, 24, 3
+ .comm min_caml_view, 24, 3
+ .comm min_caml_light, 24, 3
+ .comm min_caml_cos_v, 16, 3
+ .comm min_caml_sin_v, 16, 3
+ .comm min_caml_beam, 8, 3
+ .comm min_caml_and_net, 400, 3
+ .comm min_caml_or_net, 8, 3
+ .comm min_caml_temp, 112, 3
+ .comm min_caml_cs_temp, 128, 3
+ .comm min_caml_solver_dist, 8, 3
+ .comm min_caml_vscan, 24, 3
+ .comm min_caml_intsec_rectside, 8, 3
+ .comm min_caml_tmin, 8, 3
+ .comm min_caml_crashed_point, 24, 3
+ .comm min_caml_crashed_object, 8, 3
+ .comm min_caml_end_flag, 8, 3
+ .comm min_caml_viewpoint, 24, 3
+ .comm min_caml_nvector, 24, 3
+ .comm min_caml_rgb, 24, 3
+ .comm min_caml_texture_color, 24, 3
+ .comm min_caml_solver_w_vec, 24, 3
+ .comm min_caml_chkinside_p, 24, 3
+ .comm min_caml_isoutside_q, 24, 3
+ .comm min_caml_nvector_w, 24, 3
+ .comm min_caml_scan_d, 8, 3
+ .comm min_caml_scan_offset, 8, 3
+ .comm min_caml_scan_sscany, 8, 3
+ .comm min_caml_scan_met1, 8, 3
+ .comm min_caml_wscan, 24, 3
+
diff --git a/firmware/src/main.c b/firmware/src/main.c
index 445de92..d8490dd 100644
--- a/firmware/src/main.c
+++ b/firmware/src/main.c
@@ -19,22 +19,52 @@ void min_caml_print_int(int n) {
printf("%d", n);
}
+void min_caml_print_byte(int b) {
+ printf("%c", (char)(b & 0xff));
+}
+
+void min_caml_print_newline(void) {
+ printf("\n");
+}
+
+int min_caml_read_int(void) {
+ int n;
+ scanf("%d", &n);
+ return n;
+}
+
+float min_caml_read_float(void) {
+ float x;
+ scanf("%f", &x);
+ return x;
+}
+
+float min_caml_abs_float(float x) {
+ return (x < 0.0f) ? -x : x;
+}
+
+int min_caml_truncate(float x) {
+ return (int)x;
+}
+
int main(void) {
stdio_init_all();
gpio_init(METRO_LED_PIN);
gpio_set_dir(METRO_LED_PIN, GPIO_OUT);
- while (true) {
+ // LED を点滅させながら 10 秒待機する
+ for (int i = 0; i < 10; i++) {
gpio_put(METRO_LED_PIN, 1);
- puts("LED on");
- sleep_ms(1000);
+ sleep_ms(500);
gpio_put(METRO_LED_PIN, 0);
- puts("LED off");
- sleep_ms(1000);
-
- min_caml_start((char *)min_caml_stack, (char *)min_caml_heap);
- printf("\n");
+ sleep_ms(500);
}
+ gpio_put(METRO_LED_PIN, 1);
+
+ // MinCaml のスタート関数を呼び出す
+ min_caml_start((char *)min_caml_stack, (char *)min_caml_heap);
+
+ while (1) {}
}
diff --git a/min-rt/min-rt.ml b/min-rt/min-rt.ml
index 237f85a..373de58 100644
--- a/min-rt/min-rt.ml
+++ b/min-rt/min-rt.ml
@@ -20,6 +20,88 @@
(*MINCAML*)let rec fhalf x = x /. 2. in
(*NOMINCAML let fhalf x = x /. 2. in*)
+
+(**************** 自作のライブラリ関数 ****************)
+
+(* sin 関数 *)
+let rec sin x =
+ (* x が -PI から PI の間に収まるよう調整する *)
+ let rec adjust x =
+ if x > 3.141592653589793 then
+ adjust (x -. 6.283185307179586)
+ else if x < -3.141592653589793 then
+ adjust (x +. 6.283185307179586)
+ else
+ x
+ in
+ let x = adjust x in
+ let x2 = x *. x in
+ let x3 = x *. x2 in
+ let x5 = x3 *. x2 in
+ let x7 = x5 *. x2 in
+ let x9 = x7 *. x2 in
+ x -. x3 /. 6.0 +. x5 /. 120.0 -. x7 /. 5040.0 +. x9 /. 362880.0
+in
+
+(* cos 関数 *)
+let rec cos x =
+ (* x が -PI から PI の間に収まるよう調整する *)
+ let rec adjust x =
+ if x > 3.141592653589793 then
+ adjust (x -. 6.283185307179586)
+ else if x < -3.141592653589793 then
+ adjust (x +. 6.283185307179586)
+ else
+ x
+ in
+ let x = adjust x in
+ let x2 = x *. x in
+ let x4 = x2 *. x2 in
+ let x6 = x4 *. x2 in
+ let x8 = x6 *. x2 in
+ let x10 = x8 *. x2 in
+ 1.0 -. x2 /. 2.0 +. x4 /. 24.0 -. x6 /. 720.0 +. x8 /. 40320.0 -. x10 /. 3628800.0
+in
+
+(* 平方根 *)
+let rec sqrt s =
+ let x = s /. 2.0 in
+ let last_x = 0.0 in
+ let rec loop x last_x s =
+ if x = last_x then
+ x
+ else
+ loop ((x +. s /. x) /. 2.0) x s
+ in
+ loop x last_x s
+in
+
+(* atan 関数 *)
+(* NOTE: 使われてるのかどうかよく分からないので、ひとまず 1.0 を返してお茶を濁す *)
+let rec atan x = 1.0 in
+
+(* floor 関数 *)
+(*
+ floor(0.0) -> 0.0
+ floor(1.0) -> 1.0
+ floor(1.1) -> 1.0
+ floor(1.9) -> 1.0
+ floor(2.1) -> 2.0
+ floor(-1.0) -> -1.0
+ floor(-1.1) -> -2.0
+ floor(-1.9) -> -2.0
+*)
+let rec floor x =
+ if x >= 0.0 then
+ float_of_int (int_of_float x)
+ else
+ if x = float_of_int (int_of_float x) then
+ x
+ else
+ float_of_int (int_of_float (x -. 1.0))
+in
+
(**************** ユーティリティー関数 ****************)
(* データ構造へのアクセス関数 *)
@@ -1268,4 +1350,4 @@ in
)
in
-rt 768 768 false
+rt 10 10 false
以下のコマンドでビルドを行う。
code:sh
# ビルド用のMakefileを生成
cmake -S firmware/ -B firmware/build -DPICO_BOARD=pico2
# ビルド
cmake --build firmware/build/
ビルドが成功すると min_caml_firmware.uf2 ができるので、Metro RP2350へ書き込む。書き込んだ後、cat コマンドでシリアルの出力を待ち受ける。
code:sh
cat /dev/cu.usbmodem1101
別の端末を開き、cat コマンドで SLD ファイルのテキストをレイトレへ渡す。
code:sh
cat ball.sld > /dev/cu.usbmodem1101
正しく動いていれば、待ち受けている側の cat にレイトレ結果がPPM形式で出力される(が、なんかバグってる?)
code:sh
$ cat /dev/cu.usbmodem1101
P3
10 10
255
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
デバッグ編
PPMの出力が変なので、min-rt を OCaml で実行した場合の出力と、実機で実行した場合の出力を比較しながらデバッグする。
OCaml による min-rt の実行結果を出力する手順。
code:sh
# ball.sld のレイトレ結果を出力
make -C min-rt/ ball.ocamlopt.ppm && cat min-rt/ball.ocamlopt.ppm
実機の実行結果を出力する手順。
code:sh
# min_caml_firmware.uf2 生成
make -C min-rt/ min-rt.s && cp min-rt/min-rt.s firmware/src/min-caml-start.s && cmake --build firmware/build/
# UF2を書き込み
cp firmware/build/min_caml_firmware.uf2 /Volumes/RP2350/
# 出力の待ち受け
cat /dev/cu.usbmodem1101
# SLDファイルの読み込ませ
cat min-rt/ball.sld > /dev/cu.usbmodem1101
ball.sld で調査
以下は ball.sld のデータ。最初の3つの浮動小数点数「0.0」「0.0」「-100.0」 がスクリーンの中心座標(x, y, z)。ひとまずその数値が正しく読み込めているかを確認する。
code:sld
0.0 0.0 -100.0 0.0 0.0
1 0.0 0.0
255.0
0 3 1 0 50.0 50.0 50.0 0.0 0.0 0.0 1.0 1.0 250.0 255.0 0.0 0.0
-1
0 -1
-1
99 0 -1
-1
(OCamlの出力)
「0.0」「0.0」「-100.0」が出力されている。
code:text
0.0.-100.P3
10 10
255
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 214 0 0 239 0 0 214 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 214 0 0 255 16 16 255 90 90 255 16 16 214 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 239 0 0 255 90 90 255 255 255 255 90 90 239 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 214 0 0 255 16 16 255 90 90 255 16 16 214 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 214 0 0 239 0 0 214 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
(実機の出力)
「0.0」「0.0」「-100.0」が出力されて欲しいが、「0.0」「0.0」「0.0」が出力されている。read_float がうまく動いていないっぽい。
code:text
0.0000000.0000000.000000P3
10 10
255
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
浮動小数点数を扱う関数を呼び出す場合、通常は浮動小数点レジスタを通して引数と戻り値をやり取りするが(floatabi=hardfp)、Respberry Pi Pico のCのプログラムはでは整数レジスタを経由して浮動小数点数の引数と戻り値のやり取りを行う(floatabi=softfp)
hardfp と softfp
今の read_float はCで実装しているため、MinCaml が浮動小数点レジスタに渡した引数を見ずに整数レジスタを見にいっているのが原因っぽい。問題解決のため、C言語で実装するのは整数系の関数のみにして浮動小数点系の命令はMinCamlの実装に差し替える。
code:diff
diff --git a/firmware/src/main.c b/firmware/src/main.c
index d8490dd..84e258c 100644
--- a/firmware/src/main.c
+++ b/firmware/src/main.c
@@ -33,18 +33,13 @@ int min_caml_read_int(void) {
return n;
}
-float min_caml_read_float(void) {
- float x;
- scanf("%f", &x);
- return x;
-}
-
-float min_caml_abs_float(float x) {
- return (x < 0.0f) ? -x : x;
-}
-
-int min_caml_truncate(float x) {
- return (int)x;
+int min_caml_read_byte(void) {
+ int c = getchar();
+ if (c != EOF) {
+ return c & 0xff;
+ } else {
+ return -1; // EOF
+ }
}
int main(void) {
diff --git a/min-rt/min-rt.ml b/min-rt/min-rt.ml
index 373de58..d6af9cd 100644
--- a/min-rt/min-rt.ml
+++ b/min-rt/min-rt.ml
@@ -23,6 +23,114 @@
(**************** 自作のライブラリ関数 ****************)
+(* read_float 関数 *)
+let rec read_float _ =
+ (* 小数部分をパース *)
+ let rec parse_fraction _ =
+ let c = read_byte () in
+ if c >= 48 then
+ if c <= 57 then
+ (* 0 ~ 9 の場合 *)
+ let i = float_of_int (c - 48) in
+ i +. parse_fraction () /. 10.0
+ else
+ (* 数字以外が来たら、パースを終える *)
+ 0.0
+ else
+ (* 数字以外が来たら、パースを終える *)
+ 0.0
+ in
+ (* 整数部分をパース *)
+ let rec parse_float acc =
+ let c = read_byte () in
+ if c = 46 then
+ (* "." の場合、小数点部分を読み込む *)
+ acc +. (parse_fraction () /. 10.0)
+ else if c >= 48 then
+ if c <= 57 then
+ (* 0 ~ 9 の場合 *)
+ let i = float_of_int (c - 48) +. acc *. 10.0 in
+ parse_float i
+ else
+ (* 数字以外が来たら、パースを終える *)
+ acc
+ else
+ (* 数字以外が来たら、パースを終える *)
+ acc
+ in
+ let c = read_byte () in
+ if c = 32 then
+ (* 空白の場合、次のトークン取得へ *)
+ read_float ()
+ else if c = 9 then
+ (* TAB の場合、次のトークン取得へ *)
+ read_float ()
+ else if c = 10 then
+ (* 改行の場合、次のトークン取得へ *)
+ read_float ()
+ else if c = 13 then
+ (* 改行の場合、次のトークン取得へ *)
+ read_float ()
+ else if c = 45 then
+ (* "-" の場合 *)
+ (parse_float 0.0) *. -1.0
+ else if c >= 48 then
+ if c <= 57 then
+ (* 0 ~ 9 の場合 *)
+ parse_float (float_of_int (c - 48))
+ else
+ (* エラーっぽい値を返しておく *)
+ -111.0
+ else
+ (* エラーっぽい値を返しておく *)
+ -111.0
+in
+
+(* read_int 関数 *)
+let rec read_int _ =
+ (* 整数部分をパース *)
+ let rec parse_int acc =
+ let c = read_byte () in
+ if c >= 48 then
+ if c <= 57 then
+ (* 0 ~ 9 の場合 *)
+ let i = (c - 48) + (acc + acc + acc + acc + acc + acc + acc + acc + acc + acc) in
+ parse_int i
+ else
+ (* 数字以外が来たら、パースを終える *)
+ acc
+ else
+ (* 数字以外が来たら、パースを終える *)
+ acc
+ in
+ let c = read_byte () in
+ if c = 32 then
+ (* 空白の場合、次のトークン取得へ *)
+ read_int ()
+ else if c = 9 then
+ (* TAB の場合、次のトークン取得へ *)
+ read_int ()
+ else if c = 10 then
+ (* 改行の場合、次のトークン取得へ *)
+ read_int ()
+ else if c = 13 then
+ (* 改行の場合、次のトークン取得へ *)
+ read_int ()
+ else if c = 45 then
+ (* "-" の場合 *)
+ 0 - (parse_int 0)
+ else if c >= 48 then
+ if c <= 57 then
+ (* 0 ~ 9 の場合 *)
+ parse_int (c - 48)
+ else
+ (* エラーっぽい値を返しておく *)
+ -111
+ else
+ (* エラーっぽい値を返しておく *)
+ -111
+in
+
(* sin 関数 *)
let rec sin x =
(* x が -PI から PI の間に収まるよう調整する *)
min-rt.ml をビルドし直して...
code:sh
# min-rt.ml のコンパイル & ファームウェアのビルド
make -C min-rt/ min-rt.s && cp min-rt/min-rt.s firmware/src/min-caml-start.s && cmake --build firmware/build/
ファームウェアを実機へ書き込んで...
code:sh
cp firmware/build/min_caml_firmware.uf2 /Volumes/RP2350/
USBシリアルの出力を待ち受けて...
code:sh
cat /dev/cu.usbmodem1101 | tee ~/Desktop/contest.ppm
SLDファイルをレイトレに渡して...
code:sh
cat min-rt/contest.sld > /dev/cu.usbmodem1101
レイトレ結果がPPMフォーマットで出力される。
code:sh
$ cat /dev/cu.usbmodem1101 | tee ~/Desktop/contest.ppm
P3
10 10
255
33 2 2 32 1 1 31 0 0 31 0 0 31 0 0 59 80 105 31 0 0 31 0 0 31 0 0 31 0 0 32 1 1 166 191 46 60 47 0 31 0 0 31 0 0 31 0 0 31 0 0 31 0 0 255 57 255 31 0 0 192 203 161 31 0 0 31 0 0 188 199 157 187 1 156 31 0 0 146 88 0 225 136 0 31 0 0 31 0 0 195 9 164 193 204 162 191 5 160 189 3 158 188 2 157 109 179 0 131 214 0 183 255 36 146 88 0 31 0 0 193 0 193 193 246 193 193 0 193 193 0 193 193 246 193 24 2 68 88 145 0 0 222 222 4 243 243 125 205 0 138 176 138 193 246 193 193 0 193 193 246 193 193 246 193 193 0 193 0 0 0 0 0 0 133 255 255 1 200 200 31 0 0 193 246 193 193 246 193 193 0 193 193 246 193 193 0 193 193 246 193 0 0 0 0 165 165 0 123 123 31 0 0 31 0 0 193 246 193 193 246 193 193 0 193 193 246 193 193 0 193 193 0 193 0 0 0 193 246 193 31 0 0 31 0 0 31 0 0 193 0 193 193 246 193 193 246 193 193 246 193 193 0 193 193 246 193 193 246 193 31 0 0 31 0 0 31 0 0 138 0 138 193 0 193 193 0 193 193 0 193 193 246 193 122 0 122 0 0 0
出力が完了したら Ctr-c で終了して、contest.ppm ファイルを開く。
(10x10なのでめちゃめちゃ小さい)
https://gyazo.com/374f57b4d68e456d430282ae1ca5d29f
(256x256だとこんな感じ)
https://gyazo.com/3e12c296e6a581ce0d7cf1406999253b